﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Runtime.InteropServices;

using SsrsRenderStudio.Shared;
using SsrsRenderStudio.Renderer.Support;
using SsrsRenderStudio.KnownTypesAndModels;
using SsrsRenderStudio.Shared.Windows32API;

namespace SsrsRenderStudio.Renderer.Output.Version2010
{
    //http://nicholas.piasecki.name/blog/2009/06/drawing-o-an-in-memory-metafile-in-c-sharp/
    //http://support.microsoft.com/kb/322091    //send raw data
    //http://www.codeproject.com/KB/printing/ssrscustomprinting.aspx

   
    public sealed class PrinterOutput : MetafileOutput
    {
        private System.Diagnostics.Stopwatch _debugWatch1 = new System.Diagnostics.Stopwatch();
        private System.Diagnostics.Stopwatch _debugWatch2 = new System.Diagnostics.Stopwatch();
        private System.Diagnostics.Stopwatch _debugWatch3 = new System.Diagnostics.Stopwatch();
        
        private string _destination;
        private bool _isDirectSpool;

        //private byte[][] _renderedReport;
        //private Graphics[] _graphics;

        /// <summary>
        /// Printer Output.
        /// </summary>
        /// <param name="metafiles">Array of metafiles to print.</param>
        /// <param name="destination">Path of printer.</param>
        /// <param name="spoolDirect">Print directly through Windows spooler.  Default is FALSE.</param>
        public PrinterOutput(byte[][] renderReport, string destination, bool isDirectSpool = true) : base(renderReport) 
        {
            _destination = destination;
            _isDirectSpool = isDirectSpool;
        }

        /*
        public PrinterOutput(byte[][] renderedReport, string destination) : base(null)
        {
            _destination = destination;
            _renderedReport = renderedReport;

        }

        public PrinterOutput(Graphics[] graphics, string destination) : base(null)
        {
            _destination = destination;
            _graphics = graphics;
        }
        */

        float _stockWidth = 0.0f;
        float _stockHeight = 0.0f;
        bool _isLandscape = false;
        bool _isFlip = false;
        float _marginLeft = 0.2f;
        float _marginRight = 0.2f;
        float _marginTop = 0.2f;
        float _marginBottom = 0.2f;

        private Metafile _currentMetafile = null;
        private int _currentPrintingPage;
        private int _lastPrintingPage;
        private int _numberOfPages;

        public void SetOptions(dynamic options, bool isFlipped = false)
        {
            _isFlip = isFlipped;

            if (options.GetType() == typeof(PrintOptions))
            {
                _stockHeight = options.StockHeight;
                _stockWidth = options.StockWidth;
                _isLandscape = options.IsLandscape;
            }

            if (options.GetType() == typeof(PageSettings10))
            {
                PageSettings10 options10 = options as PageSettings10;

                _isLandscape = false;
                if (options10.Width > options10.Height) _isLandscape = true;

                float multiplier = 0.0394f;
                if (!options10.IsMetric) multiplier = 1f;

                //TODO might have to swap these
                _stockHeight = (float)(options10.Height * multiplier);
                _stockWidth = (float)(options10.Width * multiplier);

                _marginLeft = (float)(options10.MarginLeft * multiplier);
                _marginRight = (float)(options10.MarginRight * multiplier);
                _marginTop = (float)(options10.MarginTop * multiplier);
                _marginBottom = (float)(options10.MarginBottom * multiplier);

                // _rotation = (float)(options10.Rotation);
          }
        }

        public override void Process()
        {
            _debugWatch2.Restart();

            if (_isDirectSpool)
            {
                PrintReportViaSpooler2();
            }
            else
            {
                PrintReportViaPrintDocument();
            }

            _debugWatch2.Stop();
            Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Process() method " + _debugWatch2.ElapsedMilliseconds);

            /*
            if (_metafiles != null)
            {
                //PrintReportViaPrintDocument();
                PrintReportViaSpooler2();
            }
            else if(_renderedReport != null)
            {
                PrintReportViaSpooler();
            }
            else if (_graphics != null)
            {
                PrintReportViaSpoolerFromGraphics();
            }
            */
        }

        /// <summary>
        /// Sets current metafile variable to requested page.
        /// </summary>
        private bool MoveToPage(Int32 page)
        {
            _currentMetafile = _metafiles[page - 1];
            return true;
        }

        /// <summary>
        /// Print directly to spooler.
        /// </summary>
        private void PrintReportViaSpooler2()
        {
            _numberOfPages = _metafiles.Length;
            _currentPrintingPage = 1;
            _lastPrintingPage = _numberOfPages;

            IntPtr deviceContext;
            DOCINFO documentInfo;
            int hardMarginLeft;
            int hardMarginTop;

            documentInfo = new DOCINFO();
            documentInfo.fwType = 0;
            documentInfo.lpszDatatype = null;
            documentInfo.lpszDocName = "SsrsRenderStudioPrintJob";
            documentInfo.lpszOutput = null;
            documentInfo.cbSize = Marshal.SizeOf(documentInfo);

            _debugWatch1.Restart();
            
            deviceContext = CreateDC(
                "WINSPOOL",
                _destination.Normalize(),
                null,
                IntPtr.Zero);

            _debugWatch1.Stop();
            Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Creating WINSPOOL device context " + _debugWatch1.ElapsedMilliseconds);

            if (deviceContext != IntPtr.Zero)
            {
                _debugWatch1.Restart();

                hardMarginLeft = GetDeviceCaps(
                    deviceContext,
                    (int)DeviceCap.PHYSICALOFFSETX);
                hardMarginTop = GetDeviceCaps(
                    deviceContext,
                    (int)DeviceCap.PHYSICALOFFSETY);

                hardMarginLeft = (int)(hardMarginLeft * 100F / GetDeviceCaps(deviceContext, (int)DeviceCap.LOGPIXELSX));
                hardMarginTop = (int)(hardMarginTop * 100F / GetDeviceCaps(deviceContext, (int)DeviceCap.LOGPIXELSY));

                _debugWatch1.Stop();
                Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Get device information " + _debugWatch1.ElapsedMilliseconds);

                Point[] points = new Point[3] { new Point(0, 0), 
                                            new Point(Convert.ToInt16(_stockWidth * 100), 0), 
                                            new Point(0, Convert.ToInt16(_stockHeight * 100)) };

                //in inches
                //RectangleF clipPageSize = new RectangleF(0, 0, _stockWidth, _stockHeight);

                _debugWatch1.Restart();

                int startdoc = StartDoc(deviceContext, ref documentInfo);

                _debugWatch1.Stop();
                Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * StartDoc " + _debugWatch1.ElapsedMilliseconds);

                if (startdoc > 0)
                {
                    for (int i = 0; i < _numberOfPages ; ++i)
                    {
                        Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing started, timer3 started");
                        _debugWatch3.Restart();

                        MoveToPage(_currentPrintingPage);
                            
                        using (var g = Graphics.FromHdcInternal(deviceContext))
                        {
                            Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, FromHdcInternal, timer3 @ " + _debugWatch3.ElapsedMilliseconds);
                            
                            g.TranslateTransform(-hardMarginLeft, -hardMarginTop);
                            Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, TranslateTransform, timer3 @ " + _debugWatch3.ElapsedMilliseconds);

                            _debugWatch1.Restart();

                            int startpage = StartPage(deviceContext);

                            _debugWatch1.Stop();
                            Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * StartPage " + _debugWatch1.ElapsedMilliseconds);
                            Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, post StartPage, timer3 @ " + _debugWatch3.ElapsedMilliseconds);

                            if (startpage > 0)
                            {
                                float xTrans = 0f;
                                float yTrans = 0f;
                                float qRotate = 0f;

                                lock (this)
                                {
                                    if (_isLandscape)
                                    {
                                        if (_isFlip)
                                        {
                                            qRotate = 90f;
                                            yTrans = _stockHeight * -100;
                                        }
                                        else
                                        {
                                            qRotate = -90f;
                                            xTrans = _stockWidth * -100;
                                        }
                                    }
                                    else
                                    {
                                        if (_isFlip)
                                        {
                                            qRotate = 180;
                                            xTrans = _stockWidth * -100;
                                            yTrans = _stockHeight * -100;
                                        }
                                    }

                                    g.RotateTransform(qRotate);
                                    Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, RotateTransform, timer3 @ " + _debugWatch3.ElapsedMilliseconds);

                                    // move origin
                                    g.TranslateTransform(xTrans, yTrans);
                                    Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, TranslateTransform, timer3 @ " + _debugWatch3.ElapsedMilliseconds);

                                    MetafilePainterInstance painter = new MetafilePainterInstance();
                                    Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, create MetafilePainter, timer3 @ " + _debugWatch3.ElapsedMilliseconds);
                                    painter.PaintImage(g, _currentMetafile, points);
                                    Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, PaintImage, timer3 @ " + _debugWatch3.ElapsedMilliseconds);

                                    //MetafilePainter.PaintImage(g, _currentMetafile, points);
                                    
                                    // manually add the barcode
                                    //Renderer.BarcodeGenerator.PaintBarcode(g, Renderer.EnumSymbology.THREEOFNINE, "*123ABC*", new PointF(450, 300 ));

                                    //Renderer.MetafilePainter.CreateBarcode1D(203, 203, "*123ABC*");

                                    /*
                                    g.FillRectangle(new SolidBrush(Color.White), new Rectangle(new Point(40,40), new Size(300, 35)));
                                    g.DrawString("Flipped: " + _isFlip.ToString() +
                                                " X trans: " + xTrans +
                                                " Y trans: " + yTrans + 
                                                " Rotation: " + qRotate, new Font(FontFamily.GenericSerif, 10), new SolidBrush(Color.Black), new PointF(50, 50));
                                    */

                                    /*
                                    MetafileHeader mfH = _currentMetafile.GetMetafileHeader();
                                    g.ScaleTransform(mfH.DpiX / g.DpiX, mfH.DpiY / g.DpiY, System.Drawing.Drawing2D.MatrixOrder.Prepend);
                                    g.TranslateTransform(-hardMarginLeft, -hardMarginTop);
                                    g.RotateTransform(90);

                                    g.DrawImageUnscaled(_currentMetafile, new Point(0, 0));
                                    */

                                    g.Flush();
                                    Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, Flush, timer3 @ " + _debugWatch3.ElapsedMilliseconds);
                                }

                                _currentPrintingPage++;

                                _debugWatch1.Restart();

                                int endpage = EndPage(deviceContext);

                                _debugWatch1.Stop();
                                Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * EndPage " + _debugWatch1.ElapsedMilliseconds);
                                Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing inproc, post EndPage, timer3 @ " + _debugWatch3.ElapsedMilliseconds);

                                if (endpage <= 0)
                                {
                                    Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _destination + " EndPage failed.");
                                }
                            }
                            else
                            {
                                Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _destination + " StartPage failed.");
                            }
                        }

                        _debugWatch3.Stop();
                        Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Page processing finished, including StartPage & EndPage, timer3 @ " + _debugWatch3.ElapsedMilliseconds);
                    }

                    _debugWatch1.Restart();

                    int enddoc = EndDoc(deviceContext);
                    
                    _debugWatch1.Stop();
                    Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * EndDoc " + _debugWatch1.ElapsedMilliseconds);

                    if (enddoc <= 0)
                    {
                        Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _destination + " EndDoc failed.");
                    }
                }
            }
            else
            {
                //todo : need to throw some errros for REX on this.
                Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _destination + " printer not available [Win32:" + Marshal.GetLastWin32Error().ToString() + "]");
            }

            if (deviceContext != IntPtr.Zero)
            {
                _debugWatch1.Restart();

                DeleteDC(deviceContext);

                _debugWatch1.Stop();
                Logger.Log(EnumLoggingSeverity.DEBUG, "PrinterOutput: * Delete device context " + _debugWatch1.ElapsedMilliseconds);
            }
        }

        /// <summary>
        /// Print document via .NET
        /// </summary>
        private bool PrintReportViaPrintDocument()
        {
            try
            {
                Shared.Logger.WriteConsole("\t\t***prt: setting printer");
                // set page states
                _numberOfPages = _metafiles.Length;
                _currentPrintingPage = 1;
                _lastPrintingPage = _numberOfPages;

                // define printer settings
                PrinterSettings printerSettings = new PrinterSettings();
                printerSettings.MaximumPage = _numberOfPages;
                printerSettings.MinimumPage = 1;
                printerSettings.PrintRange = PrintRange.SomePages;
                printerSettings.FromPage = 1;
                printerSettings.ToPage = _numberOfPages;
                printerSettings.PrinterName = _destination;

                printerSettings.DefaultPageSettings.PaperSize=_isLandscape ? 
                                new PaperSize("Custom", (int)(_stockHeight * 100), (int)(_stockWidth * 100)) :        
                                new PaperSize("Custom", (int)(_stockWidth * 100), (int)(_stockHeight * 100));

                printerSettings.DefaultPageSettings.Landscape = _isLandscape;
                printerSettings.DefaultPageSettings.Margins.Left = (int)(_marginLeft * 100);
                printerSettings.DefaultPageSettings.Margins.Right = (int)(_marginRight * 100);
                printerSettings.DefaultPageSettings.Margins.Top = (int)(_marginTop * 100);
                printerSettings.DefaultPageSettings.Margins.Bottom = (int)(_marginBottom * 100);

                // slow get
                //Console.WriteLine("***prt: Dpi: (x) " + printerSettings.DefaultPageSettings.PrinterResolution.X.ToString() +
                //                                " x (y) " + printerSettings.DefaultPageSettings.PrinterResolution.Y.ToString());

                // define and print document
                PrintDocument pd = new PrintDocument();
                pd.PrinterSettings = printerSettings;
                pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);
                Shared.Logger.WriteConsole("\t\t***prt: hitting print");
                pd.Print();
            }
            catch (Exception ex)
            {
                Shared.Logger.WriteConsole(ex.Message);
            }

            return true;
        }

        private void pd_PrintPage(object sender, PrintPageEventArgs ev)
        {
            Shared.Logger.WriteConsole("\t\t***prt: begin print");
            // reset more pages, printer assuming this is the only page to print
            ev.HasMorePages = false;

            if (_currentPrintingPage <= _lastPrintingPage && MoveToPage(_currentPrintingPage))
            {
                
                Point[] points = new Point[3] { new Point(0, 0), 
                                                new Point(Convert.ToInt16(_stockWidth * 100), 0), 
                                                new Point(0, Convert.ToInt16(_stockHeight * 100)) };
                lock (this)
                {
                    MetafilePainterInstance painter = new MetafilePainterInstance();
                    painter.PaintImage(ev.Graphics, _currentMetafile, points);
                }
                    


                // If the next page is less than or equal to the last page, print another page.
                if (++_currentPrintingPage <= _lastPrintingPage)
                    ev.HasMorePages = true;
            }

            Shared.Logger.WriteConsole("\t\t***prt: end print");
        }

        /*
        private void pd_PrintPage(object sender, PrintPageEventArgs ev)
        {
            Console.WriteLine("***prt: begin print");
            // reset more pages, printer assuming this is the only page to print
            ev.HasMorePages = false;

            if (_currentPrintingPage <= _lastPrintingPage && MoveToPage(_currentPrintingPage))
            {
                MetafileHeader mfH  = _currentMetafile.GetMetafileHeader();
                ev.Graphics.ScaleTransform(mfH.DpiX / 203, mfH.DpiY / 203, System.Drawing.Drawing2D.MatrixOrder.Prepend);
                ev.Graphics.DrawImageUnscaled(_currentMetafile, new Point(0, 0));

                // If the next page is less than or equal to the last page, print another page.
                if (++_currentPrintingPage <= _lastPrintingPage)
                    ev.HasMorePages = true;
            }
            Console.WriteLine("***prt: end print");
        }
        */

        /*
        // Method to draw the current emf memory stream 
        private void ReportDrawPage(Graphics g)
        {
            // metafile should have been set already
            if (null == _currentMetafile)
                return;

            lock (this)
            {
                
                //int width = _isLandscape ? Convert.ToInt16(_metafile.HorizontalResolution * _stockWidth) : Convert.ToInt16(_metafile.VerticalResolution * _stockHeight);
                //int height = _isLandscape ? Convert.ToInt16(_metafile.VerticalResolution * _stockHeight) :  Convert.ToInt16(_metafile.HorizontalResolution * _stockWidth);

                //landscape or not, this stays constant
                //int width = Convert.ToInt16(_metafile.HorizontalResolution * _stockWidth);
                //int height = Convert.ToInt16(_metafile.VerticalResolution * _stockHeight);

                // any DPI is ignored ???
                // Draw in the rectangle
                int width = Convert.ToInt16(_stockWidth * 100);
                int height = Convert.ToInt16( _stockHeight * 100);
                Point[] points = new Point[3] { new Point(0, 0), new Point(width, 0), new Point(0, height) };

                Renderer.MetafilePainter.PaintImage(g, _currentMetafile, points);

                
                // moved to MetafilePainter
                //_delegate = new Graphics.EnumerateMetafileProc(MetafileCallback);
                //g.EnumerateMetafile(_currentMetafile, points, _delegate);
                //_delegate = null;
                
            }
        }
        */

        /*
        private void PrintReportViaSpooler(int copies = 1)
        {
            IntPtr deviceContext;
            DOCINFO documentInfo;
            int hardMarginLeft;
            int hardMarginTop;
            int pagesCount;

            documentInfo = new DOCINFO();
            documentInfo.fwType = 0;
            documentInfo.lpszDatatype = null;
            documentInfo.lpszDocName = "RENDERING";
            documentInfo.lpszOutput = null;
            documentInfo.cbSize = Marshal.SizeOf(documentInfo);

            pagesCount = _renderedReport.Length;

            deviceContext = CreateDC(
                "WINSPOOL",
                _destination.Normalize(),
                null,
                IntPtr.Zero);

            if (deviceContext != IntPtr.Zero)
            {
                hardMarginLeft = GetDeviceCaps(
                    deviceContext,
                    (int)DeviceCap.PHYSICALOFFSETX);
                hardMarginTop = GetDeviceCaps(
                    deviceContext,
                    (int)DeviceCap.PHYSICALOFFSETY);

                hardMarginLeft = (int)(hardMarginLeft * 100F / GetDeviceCaps(deviceContext, (int)DeviceCap.LOGPIXELSX));
                hardMarginTop = (int)(hardMarginTop * 100F / GetDeviceCaps(deviceContext, (int)DeviceCap.LOGPIXELSY));

                for (int copyIdx = 0; copyIdx < copies; ++copyIdx)
                {
                    if (StartDoc(deviceContext, ref documentInfo) > 0)
                    {
                        for (int i = 0; i < pagesCount; ++i)
                        {
                            byte[] data;

                            data = _renderedReport[i];

                            using (var ms = new MemoryStream(data))
                            using (var metafile = Metafile.FromStream(ms))
                            {
                                using (var g = Graphics.FromHdcInternal(deviceContext))
                                {
                                    g.TranslateTransform(-hardMarginLeft, -hardMarginTop);

                                    if (StartPage(deviceContext) > 0)
                                    {

                                        g.DrawImage(metafile, 0, 0);

                                        g.Flush();

                                        if (EndPage(deviceContext) <= 0)
                                        {
                                            throw new Exception();
                                        }
                                    }
                                    else
                                    {
                                        throw new Exception();
                                    }
                                }
                            }
                        }

                        if (EndDoc(deviceContext) <= 0)
                        {
                            throw new Exception();
                        }
                    }
                }
            }
            else
            {
                throw new Exception();
            }

            if (deviceContext != IntPtr.Zero)
            {
                DeleteDC(deviceContext);
            }
        }

        private void PrintReportViaSpoolerFromGraphics(int copies = 1)
        {
            IntPtr deviceContext;
            DOCINFO documentInfo;
            int hardMarginLeft;
            int hardMarginTop;
            int pagesCount;

            documentInfo = new DOCINFO();
            documentInfo.fwType = 0;
            documentInfo.lpszDatatype = null;
            documentInfo.lpszDocName = "RENDERING";
            documentInfo.lpszOutput = null;
            documentInfo.cbSize = Marshal.SizeOf(documentInfo);

            pagesCount = _graphics.Length;

            deviceContext = CreateDC(
                "WINSPOOL",
                _destination.Normalize(),
                null,
                IntPtr.Zero);

            if (deviceContext != IntPtr.Zero)
            {
                hardMarginLeft = GetDeviceCaps(
                    deviceContext,
                    (int)DeviceCap.PHYSICALOFFSETX);
                hardMarginTop = GetDeviceCaps(
                    deviceContext,
                    (int)DeviceCap.PHYSICALOFFSETY);

                hardMarginLeft = (int)(hardMarginLeft * 100F / GetDeviceCaps(deviceContext, (int)DeviceCap.LOGPIXELSX));
                hardMarginTop = (int)(hardMarginTop * 100F / GetDeviceCaps(deviceContext, (int)DeviceCap.LOGPIXELSY));

                for (int copyIdx = 0; copyIdx < copies; ++copyIdx)
                {
                    if (StartDoc(deviceContext, ref documentInfo) > 0)
                    {
                        for (int i = 0; i < pagesCount; ++i)
                        {
                            //byte[] data;

                            //data = _renderedReport[i];

                            //using (var ms = new MemoryStream(data))
                            //using (var metafile = Metafile.FromStream(ms))
                            {
                                //using (var g = Graphics.FromHdcInternal(deviceContext))
                                using (var g = _graphics[i])
                                {
                                    g.TranslateTransform(-hardMarginLeft, -hardMarginTop);

                                    if (StartPage(deviceContext) > 0)
                                    {

                                        //g.ScaleTransform(.2f, .2f);
                                        //g.SetClip(new Rectangle(0, 0, 1000, 400));
                                        // TODO: this dont work.
                                        //g.DrawImage(metafile, 0, 0);
                                        
                                        g.Flush();

                                        if (EndPage(deviceContext) <= 0)
                                        {
                                            throw new Exception();
                                        }
                                    }
                                    else
                                    {
                                        throw new Exception();
                                    }
                                }
                            }
                        }

                        if (EndDoc(deviceContext) <= 0)
                        {
                            throw new Exception();
                        }
                    }
                }
            }
            else
            {
                throw new Exception();
            }

            if (deviceContext != IntPtr.Zero)
            {
                DeleteDC(deviceContext);
            }
        }
        */

        #region pinvoke
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public class DOCINFOA
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDocName;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pOutputFile;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDataType;
        }

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct DOCINFO
        {
            public int cbSize;
            [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
            public string lpszDocName;
            [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
            public string lpszOutput;
            [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
            public string lpszDatatype;
            public uint fwType;
        }

        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool ClosePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

        [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndDocPrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateDC(string lpszDriver, string lpszDevice,
           string lpszOutput, IntPtr lpInitData);

        [DllImport("gdi32.dll")]
        static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

        [DllImport("gdi32.dll")]
        static extern int StartDoc(IntPtr hdc, [In] ref DOCINFO lpdi);

        [DllImport("gdi32.dll")]
        static extern int EndDoc(IntPtr hdc);

        [DllImport("gdi32.dll")]
        static extern int StartPage(IntPtr hDC);

        [DllImport("gdi32.dll")]
        static extern int EndPage(IntPtr hdc);

        [DllImport("gdi32.dll")]
        static extern bool DeleteDC(IntPtr hdc);

        public enum DeviceCap
        {
        /// <summary>
        /// Device driver version
        /// </summary>
        DRIVERVERSION = 0,
        /// <summary>
        /// Device classification
        /// </summary>
        TECHNOLOGY = 2,
        /// <summary>
        /// Horizontal size in millimeters
        /// </summary>
        HORZSIZE = 4,
        /// <summary>
        /// Vertical size in millimeters
        /// </summary>
        VERTSIZE = 6,
        /// <summary>
        /// Horizontal width in pixels
        /// </summary>
        HORZRES = 8,
        /// <summary>
        /// Vertical height in pixels
        /// </summary>
        VERTRES = 10,
        /// <summary>
        /// Number of bits per pixel
        /// </summary>
        BITSPIXEL = 12,
        /// <summary>
        /// Number of planes
        /// </summary>
        PLANES = 14,
        /// <summary>
        /// Number of brushes the device has
        /// </summary>
        NUMBRUSHES = 16,
        /// <summary>
        /// Number of pens the device has
        /// </summary>
        NUMPENS = 18,
        /// <summary>
        /// Number of markers the device has
        /// </summary>
        NUMMARKERS = 20,
        /// <summary>
        /// Number of fonts the device has
        /// </summary>
        NUMFONTS = 22,
        /// <summary>
        /// Number of colors the device supports
        /// </summary>
        NUMCOLORS = 24,
        /// <summary>
        /// Size required for device descriptor
        /// </summary>
        PDEVICESIZE = 26,
        /// <summary>
        /// Curve capabilities
        /// </summary>
        CURVECAPS = 28,
        /// <summary>
        /// Line capabilities
        /// </summary>
        LINECAPS = 30,
        /// <summary>
        /// Polygonal capabilities
        /// </summary>
        POLYGONALCAPS = 32,
        /// <summary>
        /// Text capabilities
        /// </summary>
        TEXTCAPS = 34,
        /// <summary>
        /// Clipping capabilities
        /// </summary>
        CLIPCAPS = 36,
        /// <summary>
        /// Bitblt capabilities
        /// </summary>
        RASTERCAPS = 38,
        /// <summary>
        /// Length of the X leg
        /// </summary>
        ASPECTX = 40,
        /// <summary>
        /// Length of the Y leg
        /// </summary>
        ASPECTY = 42,
        /// <summary>
        /// Length of the hypotenuse
        /// </summary>
        ASPECTXY = 44,
        /// <summary>
        /// Shading and Blending caps
        /// </summary>
        SHADEBLENDCAPS = 45,

        /// <summary>
        /// Logical pixels inch in X
        /// </summary>
        LOGPIXELSX = 88,
        /// <summary>
        /// Logical pixels inch in Y
        /// </summary>
        LOGPIXELSY = 90,

        /// <summary>
        /// Number of entries in physical palette
        /// </summary>
        SIZEPALETTE = 104,
        /// <summary>
        /// Number of reserved entries in palette
        /// </summary>
        NUMRESERVED = 106,
        /// <summary>
        /// Actual color resolution
        /// </summary>
        COLORRES = 108,

        // Printing related DeviceCaps. These replace the appropriate Escapes
        /// <summary>
        /// Physical Width in device units
        /// </summary>
        PHYSICALWIDTH = 110,
        /// <summary>
        /// Physical Height in device units
        /// </summary>
        PHYSICALHEIGHT = 111,
        /// <summary>
        /// Physical Printable Area x margin
        /// </summary>
        PHYSICALOFFSETX = 112,
        /// <summary>
        /// Physical Printable Area y margin
        /// </summary>
        PHYSICALOFFSETY = 113,
        /// <summary>
        /// Scaling factor x
        /// </summary>
        SCALINGFACTORX = 114,
        /// <summary>
        /// Scaling factor y
        /// </summary>
        SCALINGFACTORY = 115,

        /// <summary>
        /// Current vertical refresh rate of the display device (for displays only) in Hz
        /// </summary>
        VREFRESH = 116,
        /// <summary>
        /// Horizontal width of entire desktop in pixels
        /// </summary>
        DESKTOPVERTRES = 117,
        /// <summary>
        /// Vertical height of entire desktop in pixels
        /// </summary>
        DESKTOPHORZRES = 118,
        /// <summary>
        /// Preferred blt alignment
        /// </summary>
        BLTALIGNMENT = 119
        }
        #endregion
    }
}
